package fuse.LaCollaFS;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import fuse.FuseException;
import fuse.FuseMount;
import fuse.FuseStatfs;
import fuse.LaCollaFS.Metadades.*;
import fuse.Filesystem1;
import fuse.FuseDirEnt;
import fuse.FuseStat;

public class LaCollaFS implements Filesystem1 {

	private String defpath = "/tmp/lacollafs";

	private Organitzador org;

	private static MetaDades dades;

	/**
	 * Constructor de LaCollaFS inicialitza el api de lacolla inicialitza les
	 * metadades crea el fitxer arrel.
	 * 
	 * TODO veure com fer les reconexións o una nova conexió a l'api per
	 * compartir el dir.xml
	 * 
	 * @param user
	 *            TODO
	 * @throws IOException
	 *             TODO
	 */
	public LaCollaFS(String user) throws IOException {

		dades = new MetaDades(defpath);
		org = new Organitzador();

	}

	/**
	 * Canviar els permisos d'un node de les metadades Quan acaba desa els
	 * canvis a lacolla
	 * 
	 * @param path
	 *            path del node a modificar
	 * @param mode
	 *            integer amb els nous permisos
	 */
	public void chmod(String path, int mode) throws FuseException {

		FuseStat estat;
		int id = org.setPermisos(path);
		estat = dades.ObtenirStat(path);
		estat.mode = mode;
		dades.CanviarStat(path, estat);
		org.Fet(id);

	}

	/**
	 * Canvia el propietari/grup del objecte a les metadades
	 * 
	 * @param path
	 *            path del objecte a modificar
	 * @param uid
	 *            identificador del nou propietari
	 * @param gid
	 *            identificador del grup del nou propietari
	 */
	public void chown(String path, int uid, int gid) throws FuseException {

		FuseStat estat;
		int id = org.setPropietari(path);
		estat = dades.ObtenirStat(path);
		// si uid val -1 es que volem fer un canvi del gid, quan es un canvi en
		// el uid el que val -1 es el uid
		if (uid == -1) {
			estat.gid = gid;
		} else if (gid == -1) {
			estat.uid = uid;
		}

		dades.CanviarStat(path, estat);
		org.Fet(id);

	}

	/**
	 * Retorna el estat del node en questió
	 * 
	 * @param path
	 *            path del node a consultar el stat
	 */
	public FuseStat getattr(String path) throws FuseException {
		org.ObtenirStat(path);
		return dades.ObtenirStat(path);
	}

	/**
	 * Retorna el contingut del directori
	 * 
	 * @param path
	 *            path del directori del que volem saber els fills
	 */
	public FuseDirEnt[] getdir(String path) {
		Directori node;
		FuseDirEnt[] dir;

		int id = org.LlegirDir(path);
		node = (Directori) dades.ObtenirNode(path);
		dir = node.Llistarfills();
	
		p("nem a veure quan val el lenght del dir..."+ dir.length);
		org.Fet(id);
		return dir;

	}

	/**
	 * Crea un nou dorectori
	 * 
	 * @param path
	 *            path del nou directori, nom inclos
	 * @param mode
	 *            mode (permisos) amb que s'ha de crear el nou directori
	 */
	public void mkdir(String path, int mode) throws FuseException {

		Directori node;
		Directori nou;

		String pathNode;
		String pathNom;

		pathNode = dades.ObtenirDir(path);
		pathNom = dades.ObtenirNom(path);

		int id = org.CrearNode(path);

		node = (Directori) dades.ObtenirNode(pathNode);
		nou = new Directori(pathNom, dades);
		node.AfegirFill(nou);
		dades.DesaTree();

		org.Fet(id);

	}

	/**
	 * Elimina un directori
	 * 
	 */
	public void rmdir(String path) throws FuseException {
		// log.info("rmdir!!!!!!!!!");
		Directori node;

		int id = org.EliminarDir(path);

		node = (Directori) dades.ObtenirNode(path);
		if (node.Buit()) {
			node.getPare().EliminarFill(node.getNom());
			// dades.EliminarNode(path);
			dades.DesaTree();
		} else
			throw new FuseException("Directori no buit!!!")
					.initErrno(FuseException.ENOTEMPTY);

		org.Fet(id);

	}

	/**
	 * Crea un nou fitxer
	 */
	public void mknod(String path, int mode, int rdev) throws FuseException {

		String pathNode;
		String pathNom;
		pathNode = dades.ObtenirDir(path);
		pathNom = dades.ObtenirNom(path);

		Directori node;
		Fitxer nou;
		int id = org.CrearNode(path);

		node = (Directori) dades.ObtenirNode(pathNode);
		nou = new Fitxer(pathNom, dades);
		FuseStat estat;
		estat = nou.getStat();
		estat.mode = mode;
		nou.setStat(estat);
		node.AfegirFill(nou);
		// p("fitxer creat, ara anem a guardar el xml...");
		dades.DesaTree();

		org.Fet(id);

	}

	/**
	 * Comproba si tenim permisos per obrir el fitxer i el crea en local
	 * 
	 */
	public void open(String path, int flags) throws FuseException {
		// descargar objecte de lacolla.
		Fitxer node;
	
		int id = org.ObrirFitxer(path);
		node = (Fitxer) dades.ObtenirNode(path);
		p("creant el directori desti...");
		File direct = new File(defpath + dades.ObtenirDir(path));
		direct.mkdirs();
		p("dir creat!!!!" + direct.getAbsolutePath());
		if (node == null) {
			throw new FuseException("El fitxer no  existeix!!!")
					.initErrno(FuseException.EEXIST);
		} else {

			boolean l = false;
			boolean e = false;
			flags = flags - 32768;
			// p("flags...."+flags);
			// flags = flags -32768;
			// p("o_redonly" + O_RDONLY);
			// p("o_RDWR" + O_RDWR);
			// p("o_WRONLY" + O_WRONLY);
			if (flags == O_RDONLY) {

				l = true;
			}
			if (flags == O_RDWR) {
				l = true;
				e = true;
			}
			if (flags == O_WRONLY) {
				e = true;
			}
			p("l/e : " + l + e);
			dades.ObrirFitxer(path, l, e);
		}
		org.Fet(id);
	//	return fh;

	}

	/**
	 * llegeix un fitxer
	 */
	public void read(String path, ByteBuffer buf, long offset)
			throws FuseException {

		int id = org.LlegirFitxer(path);

		dades.Llegirfitxer(path, offset, buf);

		org.Fet(id);
	}

	/**
	 * Escriu canvis en un fitxer i ho puja a la colla quan acaba.
	 */
	public void write(String path,ByteBuffer buf, long offset) throws FuseException {

		int id = org.EscriureFitxer(path);

		dades.Escriurefitxer(path, buf, offset);

		org.Fet(id);

	}

	/**
	 * Puja els canvis d'un fitxer a lacolla i l'allibera TODO
	 */
	public void release(String path, int flags) throws FuseException {
		int id = org.DesaFitxer(path);
		dades.TancarFitxer(path);
		flags = flags - 32768;
		if (flags == O_RDWR || flags == O_WRONLY) {

			dades.PujaFitxer(path);
		}
		org.Fet(id);
	}

	/**
	 * Canvia les dates d'un Objecte a les metadades.
	 */
	public void utime(String path, int atime, int mtime) throws FuseException {
		int id = org.EscriureFitxer(path);

		Fitxer fit;
		FuseStat estat;

		fit = (Fitxer) dades.ObtenirNode(path);
		estat = fit.getStat();
		estat.atime = atime;
		estat.mtime = mtime;
		fit.setStat(estat);
		dades.DesaTree();
		org.Fet(id);
	}

	/**
	 * Crea un HardLink. Per fer-ho fa una copia del fitxer en la nova ubicació.
	 * Per a les nostres metadades, aquest link serà com si fós un fitxer més.
	 * 
	 * @param from
	 *            node origen
	 * @param to
	 *            node destí
	 */
	public void link(String from, String to) throws FuseException {

		// hard link!!!
		Fitxer nodefrom;
		Directori dirto;

		int id = org.CrearEnllas(from, to);

		dirto = (Directori) dades.ObtenirNode(dades.ObtenirDir(to));
		p("pare to es " + dirto.getNom());
		Fitxer fit = new Fitxer(dades.ObtenirNom(to), dades);
		nodefrom = (Fitxer) dades.ObtenirNode(from);
		p("fitxer from te de nom " + nodefrom.getNom());
		fit = nodefrom.copy();
		dirto.AfegirFill(fit);
		dades.DesaTree();
		org.Fet(id);

	}

	/**
	 * Crea un enllaç simbolic amb l'Objecte Enllas de les metadades
	 */
	public void symlink(String from, String to) throws FuseException {
		Estructura nodefrom;
		Directori nodeto;

		int id = org.CrearEnllas(from, to);

		nodeto = (Directori) dades.ObtenirNode(dades.ObtenirDir(to));

		Enllas en = new Enllas(dades.ObtenirNom(to), dades);
		nodefrom = dades.ObtenirNode(from);
		p("node from enlacollafs" + nodefrom);
		en.setOriginal(nodefrom);
		nodeto.AfegirFill(en);
		p("enllas creat!!!");
		dades.DesaTree();
		org.Fet(id);
	}

	/**
	 * Llegeix un link sinbolic TODO
	 * 
	 */
	public String readlink(String path) throws FuseException {

		int id = org.LlegirEnllas(path);

		Enllas en;
		Estructura node;

		en = (Enllas) dades.ObtenirNode(path);

		node = dades.ObtenirNode(en.getOriginal().ObtenirPath());

		org.Fet(id);
		return node.ObtenirPath();
	}

	/**
	 * Canvia el nom d'un Objecte TODO fer-ho tb per fitxers!!!!
	 */
	public void rename(String from, String to) throws FuseException {

		// demano permisos

		int id = org.MoureNode(from, to);
		dades.moureEstructura(from, to);

		org.Fet(id);
	}

	/**
	 * Retorna el Estat del FileSystem
	 */
	public FuseStatfs statfs() throws FuseException {
		p("statfs!!!!!!!");

		return dades.getStat();
	}

	/**
	 * Canvia el tamany d'un fitxer
	 */
	public void truncate(String path, long size) throws FuseException {

		int id = org.EscriureFitxer(path);

		Fitxer fit;
		FuseStat estat;

		fit = (Fitxer) dades.ObtenirNode(path);
		estat = fit.getStat();
		estat.size = size;
		// modificar dades!!!!
		fit.setStat(estat);
		dades.DesaTree();
		org.Fet(id);
	}

	/**
	 * Elimina un fitxer de les metadades TODO control de versions!!!
	 */
	public void unlink(String path) throws FuseException {

		int id = org.EliminarFitxer(path);

		Estructura fit = dades.ObtenirNode(path);
		// p("pare? : "+ fit.getPare().getNom());
		fit.getPare().EliminarFill(fit.getNom());
		dades.EliminarNode(path);
		dades.DesaTree();

		org.Fet(id);

	}

	/**
	 * Imprimeix per pantalla
	 * 
	 * @param o
	 */
	public void p(Object o) {
		System.out.println(o);
	}

	/**
	 * Main de la clase, crida al fusemount.
	 * 
	 * @param args
	 *            el primer parametre li passem un fitxer de text amb la
	 *            configuració de lacolla especifica en el segon parametre li
	 *            passem el punt de montatje
	 * 
	 */
	public static void main(String[] args) {
		if (args.length < 1) {
			System.out.println("Must specify params file");
			System.exit(-1);
		}

		String fuseArgs[] = new String[args.length - 1];
		System.arraycopy(args, 0, fuseArgs, 0, fuseArgs.length);

		try {
			System.out.println(" argument de lacollafs "
					+ args[args.length - 1]);

			LaCollaFS lacollafs = new LaCollaFS(fuseArgs[2]);
			FuseMount.mount(fuseArgs, lacollafs);
			dades.close();
			System.out.println("Fitxer desmontat!!!!!! i penjat....");
			lacollafs.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
		// System.out.println("Fitxer desmontat!!!!!! i penjat....");

	}

	private void close() {
		dades.close();

	}



}
